package com.example.opengl.avengine;


import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
public abstract class AVComponent {

    private static final String TAG = AVComponent.class.getSimpleName();
    public static final int RESULT_FAILED = -1;
    public static final int RESULT_OK = 0;

    public enum AVComponentType {
        VIDEO,          //视频
        AUDIO,          //音频
        TRANSACTION,    //转场
        ALL
    }

    private long engineStartTime;//引擎相关
    private long engineEndTime;//引擎相关
    private long clipStartTime;//文件相关的
    private long clipEndTime;//文件相关
    private long duration;//组件本身duration,不可改变
    private long position;//
    private boolean isOpen;
    private boolean isLoop;//是否循环播放
    private boolean isVisible;//是否可见
    private AVFrame cache;
    private AVComponentType type;
    private AtomicBoolean lockLock;
    private ReentrantLock datalock;
    private Map<String, Object> configs;
    /**记录音频数据**/

    public AVComponent(long engineStartTime, AVComponentType type) {
        this.engineStartTime = engineStartTime;
        this.engineEndTime = -1;
        this.clipStartTime = -1;
        this.clipEndTime = -1;
        this.type = type;
        this.position = Long.MAX_VALUE;
        this.isOpen = false;
        this.cache = new AVFrame();
        this.lockLock = new AtomicBoolean(false);
        this.isLoop = false;
        this.isVisible = true;
        this.datalock = new ReentrantLock();
        this.configs = new HashMap<>();
    }

    public AVComponent() {
        this.engineStartTime = -1;
        this.engineEndTime = -1;
        this.clipStartTime = -1;
        this.clipEndTime = -1;
        this.type = type;
        this.position = Long.MAX_VALUE;
        this.isOpen = false;
        this.cache = new AVFrame();

        this.lockLock = new AtomicBoolean(false);
        this.isLoop = false;
        this.isVisible = true;
        this.datalock = new ReentrantLock();
        this.configs = new HashMap<>();
    }

    public long getEngineStartTime() {
        return engineStartTime;
    }

    public void setEngineStartTime(long engineStartTime) {
        this.engineStartTime = engineStartTime;
    }

    public long getEngineEndTime() {
        return engineEndTime;
    }

    public void setEngineEndTime(long engineEndTime) {
        this.engineEndTime = engineEndTime;
    }

    public long getClipStartTime() {
        return clipStartTime;
    }

    public void setClipStartTime(long clipStartTime) {
        this.clipStartTime = clipStartTime;
    }

    public long getClipEndTime() {
        return clipEndTime;
    }

    public void setClipEndTime(long clipEndTime) {
        this.clipEndTime = clipEndTime;
    }

    public long getEngineDuration() {
        return engineEndTime - engineStartTime;
    }

    public long getClipDuration() {
        return clipEndTime - clipStartTime;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }

    public AVComponentType getType() {
        return type;
    }

    public void setType(AVComponentType type) {
        this.type = type;
    }

    public long getPosition() {
        return position;
    }

    public void setPosition(long position) {
        this.position = position;
    }

    public boolean isOpen() {
        return isOpen;
    }

    public void markOpen(boolean open) {
        isOpen = open;
    }

    public boolean isValid(long position) {
        return position >= engineStartTime && position < engineEndTime;
    }

    public boolean isLoop() {
        return isLoop;
    }

    public void setLoop(boolean loop) {
        isLoop = loop;
    }

    public boolean isVisible() {
        return isVisible;
    }

    public void setVisible(boolean visible) {
        isVisible = visible;
    }

    public Map<String, Object> getConfigs() {
        return configs;
    }

    public abstract int open();

    public abstract int close();

    /**
     * 读取一帧数据，通过peekFrame可以获取
     *
     * @return RESULT_SUCCESS/RESULT_FAILED
     */
    public abstract int readFrame();//block模式 读取一帧数据，放到AVFrame

    /**
     * 针对Engine Time
     *
     * @param position time of engine
     * @return RESULT_SUCCESS/RESULT_FAILED
     */
    public abstract int seekFrame(long position);////block模式 定位到特定位置，然后读一帧数据

    /**
     * 默认空操作
     *
     * @return RESULT_SUCCESS/RESULT_FAILED
     */
    public int write(Map<String, Object> configs) {
        this.configs.putAll(configs);
        return RESULT_OK;
    }

    /**
     * 默认空操作
     *
     * @return RESULT_SUCCESS/RESULT_FAILED
     */
    public int write(Object... configs) {
        if (configs == null || configs.length % 2 != 0) {
            return RESULT_FAILED;
        }
        for (int i = 0; i < configs.length / 2; i++) {
            this.configs.put((String) configs[2 * i], configs[2 * i + 1]);
        }
        return RESULT_OK;
    }

    /**
     * 获取一帧数据
     *
     * @return AVFrame
     */
    public AVFrame peekFrame() {
        return cache;
    }

    public int markRead() {//消费一帧数据，将AVFrame set valid = false
        if (!isOpen()) return RESULT_FAILED;
        cache.setValid(false);
        return RESULT_OK;
    }


    public void lock() {
        datalock.lock();
    }

    public void unlock() {
        datalock.unlock();
    }

    @Override
    public String toString() {
        return "AVComponent{\n" +
                "\tengineStartTime=" + engineStartTime + "\n" +
                "\tengineEndTime=" + engineEndTime + "\n" +
                "\tclipStartTime=" + clipStartTime + "\n" +
                "\tclipEndTime=" + clipEndTime + "\n" +
                "\tduration=" + duration + "\n" +
                "\tposition=" + position + "\n" +
                "\tisOpen=" + isOpen + "\n" +
                "\tisLoop=" + isLoop + "\n" +
                "\tisVisible=" + isVisible + "\n" +

                "\tcache=" + cache + "\n" +
                "\ttype=" + type + "\n" +
                "\tlockLock=" + lockLock + "\n" +
                "\tdatalock=" + datalock + "\n" +
                '}' + "\n";
    }
}

